package de.dhbw.karlsruhe.picsim.model.timer;

import de.dhbw.karlsruhe.picsim.model.cpu.FileMutable;
import de.dhbw.karlsruhe.picsim.model.cpu.Interrupt;
import de.dhbw.karlsruhe.picsim.model.cpu.InterruptFacade;
import de.dhbw.karlsruhe.picsim.model.cpu.ReadOnlyFile;
import de.dhbw.karlsruhe.picsim.model.io.External;
import de.dhbw.karlsruhe.picsim.model.memory.impl.RegisterValueImpl;
import de.dhbw.karlsruhe.picsim.util.BitsMutable;
import de.dhbw.karlsruhe.picsim.util.impl.BitsMutableImpl;
import de.dhbw.karlsruhe.picsim.util.impl.Lengths;
import org.jetbrains.annotations.NotNull;

/**
 * Created by IntelliJ IDEA.
 * User: goesen
 * Date: Apr 28, 2010
 * Time: 8:04:07 PM
 */
public final class TimerFacadeImpl implements TimerFacade {
    private final ReadOnlyFile option;
    private final ResetAbleTimer preScaler;
    private final Timer timer0Control;
    private final ResetAbleTimer watchdog;
    private final ObservableTimer dogControl;

    public TimerFacadeImpl(final ReadOnlyFile option, @NotNull final FileMutable timerFile,
                           @NotNull final External external, @NotNull final FileMutable status,
                           final InterruptFacade interrupt) {
        this.option = option;
        final ObservableTimer timer0 = new Timer0(timerFile);
        preScaler = new PreScaler(option);
        timer0Control = new Timer0Control(option, preScaler, timer0);
        watchdog = new Watchdog(Lengths.WATCHDOG_TIMEOUT_DURATION);
        dogControl = new WatchDogControl(option, preScaler, watchdog);
        external.addListener(new ExternalClock(option, timer0Control));
        timer0.addTimeoutListener(new ObservableTimer.TimeoutListener() {
            @Override
            public void timeoutOccurred() {
                interrupt.putInterrupt(Interrupt.TIMER);

            }
        });
        addWatchdogTimeOutListener(new ObservableTimer.TimeoutListener() {
            @Override
            public void timeoutOccurred() {
                final BitsMutable bitsMutable = new BitsMutableImpl(status.getData());
                bitsMutable.setBit(4, false);
                status.setValue(new RegisterValueImpl(bitsMutable));
            }
        });


    }

    @Override
    public void addWatchdogTimeOutListener(@NotNull ObservableTimer.TimeoutListener listener) {
        dogControl.addTimeoutListener(listener);

    }

    @Override
    public void removeWatchdogTimeOutListener(@NotNull ObservableTimer.TimeoutListener listener) {
        dogControl.removeTimeoutListener(listener);

    }

    @Override
    public void circle() {
        if (isTimerMode()) {
            timer0Control.increment();
        }
        dogControl.increment();


    }

    @Override
    public void writeToTimerRegister() {
        if (!WatchDogControl.isPreScalerAssignedToWatchdog(option)) {
            preScaler.reset();
        }
    }

    @Override
    public void clearWatchdog() {
        if (WatchDogControl.isPreScalerAssignedToWatchdog(option)) {
            preScaler.reset();
        }
        watchdog.reset();


    }

    private boolean isTimerMode() {
        return !ExternalClock.isExternalClockMode(option);
    }
}
